using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Windows.Media.Imaging; // 添加对BitmapImage的引用
using Tools;

namespace BingAuto
{
    public class BingServer
    {
        // 定义服务状态枚举
        public enum ServerStatus
        {
            Init,
            GettingImage,  // 正在获取图片
            WaitingRetry,  // 等待重试状态（Task.Delay）
            Sleeping       // 睡眠状态（长时休眠到第二天）
        }
        
        // 定义状态变更事件委托和事件
        public delegate void StatusChangedHandler(object sender, ServerStatus status);
        public event StatusChangedHandler StatusChangedEvent;
        
        // 当前服务状态
        private ServerStatus _currentStatus = ServerStatus.Init;
        public ServerStatus CurrentStatus
        {
            get { return _currentStatus; }
            private set
            {
                if (_currentStatus != value)
                {
                    _currentStatus = value;
                    // 触发状态变更事件
                    StatusChangedEvent?.Invoke(this, _currentStatus);
                }
            }
        }
        private ServerStatus previousStatus;
        // 后台任务相关
        private CancellationTokenSource _cancellationTokenSource = null;
        private Task _backgroundTask = null;
        private DateTime _nextCheckTime = DateTime.MinValue;

        // 请求队列相关
        private Queue<GetBingImageRequest> _requestQueue = new Queue<GetBingImageRequest>();
        private object _queueLock = new object();
        private bool _isProcessingQueue = false;

        public delegate void GetImageHandler(object sender, GetImageEventArgs eventArgs);
        public event GetImageHandler GetImageEvent;

        // 请求队列的数据结构
        private class GetBingImageRequest
        {
            public DateTime? Date { get; set; }
            public bool SetAsWallpaper { get; set; }
            public GetImageEventArgs Args { get; set; }
            public TaskCompletionSource<bool> CompletionSource { get; set; }
            public bool IsArgsVersion { get; set; }
        }

        // idx表示日期偏移量，最大为7；n表示获取图片的数量，最大为8
        //string apiUrl = "https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN"; // 1080
        string apiUrl = "https://cn.bing.com/HPImageArchive.aspx?format=js&idx={0}&n={1}&mkt=zh-CN&nc=1614319565639&pid=hp&FORM=BEHPTB&uhd=1&uhdwidth=3840&uhdheight=2160"; // 4K

        // 本应用的缓存路径
        static public string appCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "BingAuto");
        static string imageFileFolder = Path.Combine(appCachePath, "Images");
        public string updateDate;//记录当前壁纸日期
        private CSVRecordManager csvData;
        private int getTodayTryTimes = 0;

        public BingServer()
        {
            if (!Directory.Exists(imageFileFolder))//如果不存在就创建file文件夹
            {
                Directory.CreateDirectory(imageFileFolder);
            }
            string csvFilePath = Path.Combine(appCachePath, "data.csv");
            csvData = new CSVRecordManager(csvFilePath);
        }

        // 获取今日的图片记录（如果存在且文件有效）
        public GetImageEventArgs GetTodayImageRecord()
        {
            string todayDate = DateTime.Now.ToString("yyyyMMdd");
            GetImageEventArgs todayRecord = csvData.FindRecordByDate(todayDate);
            
            // 检查记录是否存在且文件有效
            if (todayRecord != null && !string.IsNullOrEmpty(todayRecord.filePath) && File.Exists(todayRecord.filePath))
            {
                return todayRecord;
            }
            
            return null;
        }

        public void Start()
        {
            // 停止已有的后台任务
            Stop();
            
            // 创建新的取消令牌源和后台任务
            _cancellationTokenSource = new CancellationTokenSource();
            _backgroundTask = RunBackgroundTaskAsync(_cancellationTokenSource.Token);
        }
        
        public void Stop()
        {
            // 请求取消后台任务
            if (_cancellationTokenSource != null)
            {
                _cancellationTokenSource.Cancel();
                _cancellationTokenSource.Dispose();
                _cancellationTokenSource = null;
            }
            
            // 等待后台任务完成（如果正在运行）
            if (_backgroundTask != null && !_backgroundTask.IsCompleted)
            {
                try
                {
                    // 不等待太长时间，避免阻塞UI
                    Task.WaitAny(_backgroundTask, Task.Delay(1000));
                }
                catch (Exception ex)
                {
                    Logger.LogException("BingServer", ex);
                }
                _backgroundTask = null;
                Logger.Info("BingServer", "服务已停止");
            }
        }

        // 配置并启用下一次检查
        private void ConfigureNextCheck()
        {
            DateTime now = DateTime.Now;
            
            // 设置下一次检查时间
            if (updateDate == now.ToString("yyyyMMdd"))
            {
                // 如果已经获取了今日图片，则设置下一次检查为明天早上6点
                _nextCheckTime = now.Date.AddDays(1).AddHours(6);
                CurrentStatus = ServerStatus.Sleeping;
                getTodayTryTimes = 0;
            }
            else
            {
                // 如果未获取今日图片，则设置下一次检查为当前时间加1分钟
                _nextCheckTime = now.AddMinutes(1);
                CurrentStatus = ServerStatus.WaitingRetry;
                getTodayTryTimes++;
            }
            Logger.Info("BingServer", $"设置下一次检查时间为：{_nextCheckTime}  更新状态为：{CurrentStatus}");
        }

        // 后台任务主体
        private async Task RunBackgroundTaskAsync(CancellationToken cancellationToken)
        {
            try
            {
                Logger.Info("BingServer", "启动服务");
                
                // 任务启动时立即检查一次是否有今日的有效图片记录
                GetImageEventArgs todayRecord = GetTodayImageRecord();
                if (todayRecord != null)
                {
                    updateDate = todayRecord.updateDate;
                    // 如果存在今日有效图片，则直接更新UI显示
                    GetImageEvent?.Invoke(this, todayRecord);
                    Logger.Info("BingServer", "找到今日有效图片记录，无需重新获取");
                }
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        if (updateDate != DateTime.Now.ToString("yyyyMMdd"))
                        {
                            if(getTodayTryTimes == 0)
                            {
                                Logger.Info("BingServer", $"需要获取新图片，导航到今天以准备获取");
                                GetImageEvent?.Invoke(this, new GetImageEventArgs("", "GoToToday", "", "", ""));
                            }
                            Logger.Info("BingServer", $"请求获取（第{getTodayTryTimes + 1}次）");
                            await GetBingImage(null, true);
                        }
                        else if (CurrentStatus == ServerStatus.WaitingRetry) // 如果等待重试时手动获取成功，则设置一次壁纸
                        {
                            todayRecord = GetTodayImageRecord();
                            Utils.SetDesktopImage(todayRecord.filePath);
                            Logger.Info("BingServer", "等待重试期间手动获取成功，设置壁纸");
                        }
                        // 配置下一次检查
                        ConfigureNextCheck();
                        
                        // 计算下一次检查前的等待时间
                        TimeSpan waitTime;
                        if (_nextCheckTime != DateTime.MinValue)
                        {
                            waitTime = _nextCheckTime - DateTime.Now;
                            // 确保等待时间至少为1秒
                            if (waitTime.TotalMilliseconds < 1000)
                            {
                                waitTime = TimeSpan.FromSeconds(1);
                            }
                        }
                        else
                        {
                            // 如果没有设置下一次检查时间，默认为1分钟后
                            waitTime = TimeSpan.FromMinutes(1);
                        }
                        
                        Logger.Info("BingServer", $"进入等待状态，等待时间: {waitTime.TotalMinutes:F2} 分钟");
                        
                        // 等待指定时间，同时监听取消请求
                        await Task.Delay(waitTime, cancellationToken);
                    }
                    catch (OperationCanceledException)
                    {
                        // 任务被取消，正常退出循环
                        Logger.Info("BingServer", "检测到取消请求，准备退出");
                        break;
                    }
                    catch (Exception ex)
                    {
                        // 记录异常并继续
                        Logger.LogException("BingServer", ex);
                        
                        // 出错时，状态设置为等待重试
                        CurrentStatus = ServerStatus.WaitingRetry;
                        Logger.Info("BingServer", "处理异常完成，状态设置为等待重试");
                        
                        // 等待1分钟后重试
                        try
                        {
                            await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
                        }
                        catch (OperationCanceledException)
                        {
                            // 如果在等待期间收到取消请求，退出循环
                            Logger.Info("BingServer", "等待期间检测到取消请求，准备退出");
                            break;
                        }
                    }
                }
            }
            finally
            {
                Logger.Info("BingServer", "服务已停止");
            }
        }
        private void GettingImageState_Enter()
        {
            if(CurrentStatus != ServerStatus.GettingImage)
            {
                previousStatus = CurrentStatus;
                CurrentStatus = ServerStatus.GettingImage;
            }
        }
        private void GettingImageState_Exit()
        {
            CurrentStatus = previousStatus;
        }

        /// <summary>
        /// 内部方法：异步获取Bing图片（支持当天或指定日期）
        /// </summary>
        /// <param name="date">可选，日期，不提供则获取当天图片</param>
        /// <param name="setAsWallpaper">是否设置为壁纸，仅当天图片有效</param>
        /// <returns>是否成功获取壁纸</returns>
        private async Task<bool> GetBingImageInternal(DateTime? date, bool setAsWallpaper = false)
        {
            string filePath = "";
            GetImageEventArgs args = null;
            string requestUrl = "";
            int api_n = 1;
            int api_idx = 0;
            DateTime targetDate = date?.Date ?? DateTime.Now.Date;
            string dateString = targetDate.ToString("yyyyMMdd");
            Logger.Info("图片获取", $"获取指定日期图片: {dateString}");

            // 设置状态为正在获取图片
            GettingImageState_Enter();
            try
            {
                TimeSpan diff = DateTime.Now.Date - targetDate;
                int daysDiff = diff.Days;
                api_idx = daysDiff;
                Logger.Info("图片获取", $"计算日期差: {daysDiff} 天, API索引: {api_idx}");

                // 限制获取历史图片的范围（Bing API通常只提供最近30天的图片）
                if (daysDiff > 14)
                {
                    Logger.Warning("图片获取", "日期超出范围（超过15天）");
                    GetImageEvent?.Invoke(this, new GetImageEventArgs(dateString, "Error", "无法获取该日期的图片（只支持最近15天）", "", ""));
                    return false;
                }
                else if (daysDiff > 7)
                {
                    api_n += daysDiff - 7;
                    api_idx = 7;
                    Logger.Info("图片获取", $"调整API参数: n={api_n}, idx={api_idx}");
                }
                // 构造请求URL
                requestUrl = string.Format(apiUrl, api_idx, api_n);
                Logger.Info("图片获取", $"构造请求URL: {requestUrl}");
                // 异步请求并且解析
                string retString = await Task.Run(() => Utils.GetHtml(requestUrl));
                if (string.IsNullOrEmpty(retString))
                {
                    Logger.Error("图片获取", "API返回空数据");
                    GetImageEvent?.Invoke(this, new GetImageEventArgs(dateString, "Error", "网络异常", "", ""));
                    return false;
                }

                if (retString.Contains("访问被拒绝"))
                {
                    Logger.Error("图片获取", "API访问被拒绝");
                    GetImageEvent?.Invoke(this, new GetImageEventArgs(dateString, "Error", "异常：url被拦截", "", ""));
                    return false;
                }

                //解析 JSON
                JObject data = JObject.Parse(retString);
                JObject images = (JObject)data["images"][api_n - 1];

                //获取所需信息
                string enddate = images["enddate"].ToString();
                
                if(dateString != enddate)
                {
                    // 前面已经限制了使用国内API，及获取天数，理论上不会出现该错误
                    Logger.Error("图片获取", "API解析到错误的日期");
                    GetImageEvent?.Invoke(this, new GetImageEventArgs(dateString, "Error", "API解析到错误的日期！", "", ""));
                    return false;
                }
                string url = images["url"].ToString();
                string copyright = images["copyright"].ToString();
                copyright = copyright.Replace(",", "，");
                string title = images["title"].ToString();
                Logger.Info("图片获取", $"获取到图片信息 - 图片日期: {enddate} 标题: {title}");

                //图片下载url
                string imgurl = "https://cn.bing.com" + url;
                Logger.Info("图片获取", $"图片URL: {imgurl}");

                //如果当前获取到的信息国际版，则不下载，仅显示
                if (!url.Contains("_ZH"))
                {
                    Logger.Info("图片获取", "检测到国际版图片，仅显示不下载");
                    GetImageEvent?.Invoke(this, new GetImageEventArgs(enddate, title, copyright, "", imgurl));
                    return false;
                }
                else
                {
                    //图片存储url
                    string filename = url.Substring(url.IndexOf('=') + 1, url.IndexOf(".jpg") - url.IndexOf('=') + 3);
                    filename = filename.Insert(filename.IndexOf(".jpg"), String.Format("-{0}", enddate));
                    filePath = Path.Combine(imageFileFolder, filename);

                    //异步下载及应用
                    bool downloadResult = await Task.Run(() => Utils.DownloadFile(imgurl, filePath));
                    if (downloadResult)//下载成功
                    {
                        Logger.Info("图片获取", $"图片下载成功，保存路径: {filePath}");
                        //下载成功才记录
                        updateDate = enddate;
                        args = new GetImageEventArgs(enddate, title, copyright, filename, imgurl);
                        csvData.WriteRecord(args);
                        if (setAsWallpaper)
                        {
                            int set = Utils.SetDesktopImage(filePath);
                            if (set != 1)
                            {
                                Logger.Warning("图片获取", $"壁纸设置失败，错误码: {set}");
                                args.copyright += String.Format(" 设置失败：{0}", set.ToString()) + filePath;
                            }
                            else
                            {
                                Logger.Info("图片获取", "壁纸设置成功");
                            }
                        }
                        //更新UI
                        Logger.Info("图片获取", "触发GetImageEvent事件，通知UI更新");
                        GetImageEvent?.Invoke(this, args);
                    }
                    else
                    {
                        Logger.Info("图片获取", "图片下载失败");
                    }
                }
            }
            catch (Exception e)
            {
                Logger.LogException("图片获取", e);
                GetImageEvent?.Invoke(this, new GetImageEventArgs(dateString, "Error", String.Format("异常：{0}", e.Message), "", ""));
                GettingImageState_Exit();
                return false;
            }
            finally
            {
                // 全部线程获取完成后恢复Server之前睡眠状态
                GettingImageState_Exit();
            }
            return true;
        }
        /// <summary>
        /// 内部方法：通过记录信息获取图片，该方式支持获取7天前的图片
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        private async Task<bool> GetBingImageInternal(GetImageEventArgs args)
        {
            Logger.Info("图片获取", $"开始获取指定记录的图片，日期: {args.updateDate}");
            GettingImageState_Enter();
            //异步下载
            bool downloadResult = await Task.Run(() => Utils.DownloadFile(args.imageUrl, args.filePath));
            GettingImageState_Exit();
            if (downloadResult)//下载成功
            {
                GetImageEvent?.Invoke(this, args);
                return true;
            }
            Logger.Warning("图片获取", "图片下载失败");
            return false;
        }

        // 公共方法：将获取图片的请求加入队列，排队执行
        public Task<bool> GetBingImage(DateTime? date = null, bool setAsWallpaper = false)
        {
            Logger.Info("请求处理", $"创建获取图片请求，日期: {(date != null ? date.Value.ToString("yyyyMMdd") : "当天")}, 设置壁纸: {setAsWallpaper}");
            var request = new GetBingImageRequest
            {
                Date = date,
                SetAsWallpaper = setAsWallpaper,
                CompletionSource = new TaskCompletionSource<bool>(),
                IsArgsVersion = false
            };

            EnqueueRequest(request);
            return request.CompletionSource.Task;
        }

        // 公共方法：将通过记录信息获取图片的请求加入队列，排队执行
        public Task<bool> GetBingImage(GetImageEventArgs args)
        {
            var request = new GetBingImageRequest
            {
                Args = args,
                CompletionSource = new TaskCompletionSource<bool>(),
                IsArgsVersion = true
            };

            EnqueueRequest(request);
            return request.CompletionSource.Task;
        }

        // 辅助方法：从请求中提取日期信息
        private string GetRequestDate(GetBingImageRequest request)
        {
            if (request == null)
                return null;
            
            // 根据请求类型提取日期
            if (request.IsArgsVersion && request.Args != null)
            {
                return request.Args.updateDate;
            }
            else if (request.Date.HasValue)
            {
                return request.Date.Value.ToString("yyyyMMdd");
            }
            else
            {
                // 如果Date为null，则视为请求当天图片
                return DateTime.Now.ToString("yyyyMMdd");
            }
        }
        
        // 将请求加入队列
        private void EnqueueRequest(GetBingImageRequest request)
        {
            lock (_queueLock)
            {
                // 检查队列中是否已存在相同日期的请求
                bool isDuplicate = false;
                GetBingImageRequest existingRequest = null;
                
                // 获取当前请求的日期
                string requestDate = GetRequestDate(request);
                
                if (!string.IsNullOrEmpty(requestDate))
                {
                    // 遍历队列中的请求，检查是否有相同日期的请求
                    foreach (var queuedRequest in _requestQueue)
                    {
                        string queuedDate = GetRequestDate(queuedRequest);
                        
                        // 只要日期相同，就视为重复请求，不关心请求类型
                        if (requestDate == queuedDate)
                        {
                            isDuplicate = true;
                            existingRequest = queuedRequest;
                            break;
                        }
                    }
                }
                
                if (isDuplicate && existingRequest != null)
                {
                    // 存在重复请求，直接忽略（不共享结果，也不加入队列）
                    Logger.Warning("队列管理", $"检测到重复请求，日期: {requestDate}, 已取消");
                    // 设置请求为已取消状态
                    request.CompletionSource.SetCanceled();
                    return;
                }
                else
                {
                    // 不存在重复请求，加入队列
                    _requestQueue.Enqueue(request);
                    Logger.Info("队列管理", $"请求加入队列成功，当前队列长度: {_requestQueue.Count}");
                    
                    // 如果队列未在处理中，则开始处理
                    if (!_isProcessingQueue)
                    {
                        _isProcessingQueue = true;
                        Task.Run(async () => await ProcessRequestQueue());
                    }
                }
            }
        }

        // 处理请求队列
        private async Task ProcessRequestQueue()
        {
            while (true)
            {
                GetBingImageRequest request = null;
                
                lock (_queueLock)
                {
                    if (_requestQueue.Count == 0)
                    {
                        _isProcessingQueue = false;
                        break;
                    }
                    
                    request = _requestQueue.Dequeue();
                }
                
                try
                {
                    bool result;
                    string requestDate = GetRequestDate(request);
                    
                    if (request.IsArgsVersion)
                    {
                        result = await GetBingImageInternal(request.Args);
                    }
                    else
                    {
                        result = await GetBingImageInternal(request.Date, request.SetAsWallpaper);
                    }
                    request.CompletionSource.SetResult(result);
                }
                catch (Exception ex)
                {
                    Logger.LogException("队列处理", ex);
                    request.CompletionSource.SetException(ex);
                }
            }
        }

        public GetImageEventArgs GetBingImageRecord(DateTime date)
        {
            string dateString = date.ToString("yyyyMMdd");
            return csvData.FindRecordByDate(dateString);
        }

        // 定义在TalonBingServer类内部的事件参数类，这样可以访问fileFolder
        public class GetImageEventArgs : EventArgs
        {
            public string updateDate;
            public string title;
            public string copyright;
            public string filePath;
            public string fileName;
            public string imageUrl;

            public GetImageEventArgs(string updateDate, string title, string copyright, string fileName, string imageUrl)
            {
                this.updateDate = updateDate;
                this.title = title;
                this.copyright = copyright;
                this.fileName = fileName;
                this.imageUrl = imageUrl;
                this.filePath = Path.Combine(imageFileFolder, fileName);
            }
        }
    }
}